/*
 * RtpMediaSource.cpp
 *
 *  Created on: 2019-04-06
 *      Author: chuanjiang.zh
 */

#include "RtpMediaSource.h"
#include "MultiRtpMediaChannel.h"
#include "URI.h"
#include "TProperties.h"
#include "CodecName.h"
#include "Base64.h"
#include "H264NaluParser.h"
#include "RtpUrl.h"
#include "LinkMediaSink.h"
#include "TFileUtil.h"


namespace av
{

RtpMediaSource::RtpMediaSource():
    m_videoMedium(),
    m_audioMedium(),
    m_protocol(av::kUdp)
{
    m_mediaSink.reset(new LinkMediaSink(this));
}

RtpMediaSource::~RtpMediaSource()
{
    close();
}

int RtpMediaSource::open(const std::string& url, const std::string& params)
{
    if (!parseUrl(url))
    {
        return EINVAL;
    }

    if (!buildFormat())
    {
        return EINVAL;
    }

    if (m_format.hasVideo())
    {
        std::string videoUrl = RtpUrl::makeUrl("0.0.0.0", m_videoMedium.port);
        m_videoChannel.reset(new MultiRtpMediaChannel(m_protocol));
        m_videoChannel->open(videoUrl, std::string());
        m_videoChannel->setPeerMedium(RtpMedium::VIDEO, m_videoMedium.payload, m_videoMedium.codec, m_videoMedium.clockRate, m_videoMedium.params);

        m_videoChannel->addSink(m_mediaSink);

        m_videoChannel->setStreamMode(RtpMedium::kRecvOnly);
    }

    if (m_format.hasAudio())
    {
        std::string audioUrl = RtpUrl::makeUrl("0.0.0.0", m_audioMedium.port);
        m_audioChannel.reset(new MultiRtpMediaChannel(m_protocol));
        m_audioChannel->open(audioUrl, std::string());
        m_audioChannel->setPeerMedium(RtpMedium::AUDIO, m_audioMedium.payload, m_audioMedium.codec, m_audioMedium.clockRate, m_audioMedium.params);

        m_audioChannel->addSink(m_mediaSink);

        m_audioChannel->setStreamMode(RtpMedium::kRecvOnly);
    }

    return 0;
}

void RtpMediaSource::close()
{
    if (m_videoChannel)
    {
        m_videoChannel->close();

        m_videoChannel->removeSinks();

        m_videoChannel.reset();
    }

    if (m_audioChannel)
    {
        m_audioChannel->close();

        m_audioChannel->removeSinks();

        m_audioChannel.reset();
    }
}

bool RtpMediaSource::isOpen()
{
    return m_format.isValid();
}

int RtpMediaSource::play()
{
    setState(STATE_PLAYING);

    if (m_videoChannel)
    {
        m_videoChannel->startStream();
    }

    if (m_audioChannel)
    {
        m_audioChannel->startStream();
    }

    return 0;
}

int RtpMediaSource::pause()
{
    setState(STATE_PAUSED);

    return 0;
}

void RtpMediaSource::stop()
{
    setState(STATE_STOPPED);

    if (m_videoChannel)
    {
        m_videoChannel->stopStream();
    }

    if (m_audioChannel)
    {
        m_audioChannel->stopStream();
    }
}

int64_t RtpMediaSource::getTime()
{
    return 0;
}

int RtpMediaSource::read(AVPacket& pkt)
{
    MediaPacketPtr packet = m_pktQueue.pop();
    if (!packet)
    {
        return AVERROR(EAGAIN);
    }

    av_packet_ref(&pkt, packet.get());

    //comn::FileUtil::write(pkt.data, pkt.size, "out.h264", true);

    return 0;
}

void RtpMediaSource::interrupt()
{
    m_pktQueue.cancelWait();
}

bool RtpMediaSource::parseUrl(const std::string& url)
{
    util::Uri uri(url);
    comn::Properties props;
    props.parse(uri.m_query, '&', '=');

    m_videoMedium.ip = uri.m_host;
    m_videoMedium.clockRate = 90000;
    props.get("port", m_videoMedium.port);
    props.get("payload", m_videoMedium.payload);
    props.get("codec", m_videoMedium.codec);

    props.get("sprop", m_videoMedium.params);
    convertUrlBase64(m_videoMedium.params);


    m_audioMedium.ip = uri.m_host;
    m_audioMedium.channel = 1;
    m_audioMedium.clockRate = 8000;

    props.get("aport", m_audioMedium.port);
    props.get("apayload", m_audioMedium.payload);
    props.get("acodec", m_audioMedium.codec);
    props.get("aclock", m_audioMedium.clockRate);
    props.get("achl", m_audioMedium.channel);

    props.get("asprop", m_audioMedium.params);
    convertUrlBase64(m_audioMedium.params);

    std::string protocol;
    props.get("protocol", protocol);
    if (protocol.size() > 0)
    {
        if (comn::StringUtil::icompare(protocol, "rtp"))
        {
            m_protocol = kRtcp;
        }
        else if (comn::StringUtil::icompare(protocol, "rtcp"))
        {
            m_protocol = kRtcp;
        }
        else if (comn::StringUtil::icompare(protocol, "tcp"))
        {
            m_protocol = kTcpClient;
        }
    }

    return m_videoMedium.port > 0 || m_audioMedium.port > 0;
}

bool RtpMediaSource::buildFormat()
{
    m_format.m_clockRate = m_videoMedium.clockRate;
    m_format.m_codec = CodecName::parse(m_videoMedium.codec);
    if (m_videoMedium.params.size() > 0)
    {
        if (m_format.m_codec == MEDIA_CODEC_H264)
        {
            std::string sprop;
            std::string spsData;
            std::string ppsData;
            comn::StringUtil::split(m_videoMedium.params, ',', spsData, ppsData);
            if (spsData.size())
            {
                H264NaluParser::insertStartCode(sprop);
                decodeBase64(spsData, sprop);
            }

            if (ppsData.size())
            {
                H264NaluParser::insertStartCode(sprop);
                decodeBase64(ppsData, sprop);
            }

            m_format.setVideoProp(sprop.c_str(), sprop.size());
        }
        else if (m_format.m_codec == MEDIA_CODEC_HEVC)
        {
            std::string sprop;
            std::vector<std::string> sections;
            comn::StringUtil::split(m_videoMedium.params, ',', sections);
            if (sections.size() == 3)
            {
                std::string& vps = sections[0];
                std::string& sps = sections[1];
                std::string& pps = sections[2];

                H264NaluParser::insertStartCode(sprop);
                decodeBase64(vps, sprop);
                H264NaluParser::insertStartCode(sprop);
                decodeBase64(sps, sprop);
                H264NaluParser::insertStartCode(sprop);
                decodeBase64(pps, sprop);
            }
            
            m_format.setVideoProp(sprop.c_str(), sprop.size());
        }
    }

    m_format.m_audioCodec = CodecName::parse(m_audioMedium.codec);
    m_format.m_channels = m_audioMedium.channel;
    m_format.m_sampleRate = m_audioMedium.clockRate;
    if (m_audioMedium.params.size())
    {
        std::string config;
        decodeBase64(m_audioMedium.params, config);
        m_format.setAudioConfig(config.c_str(), config.size());
    }

    return m_format.m_codec > 0 || m_format.m_audioCodec > 0;
}

void RtpMediaSource::convertUrlBase64(std::string& str)
{
    for (size_t i = 0; i < str.size(); i++)
    {
        char& ch = str[i];
        if (ch == '*')
        {
            ch = '+';
        }
        else if (ch == '-')
        {
            ch = '/';
        }
        else if (ch == '.')
        {
            ch = '.';
        }
    }
}

void RtpMediaSource::decodeBase64(const std::string& str, std::string& out)
{
    uint8_t buffer[1024] = { 0 };
    int size = comn::Base64::decode(buffer, 1024, str.c_str(), str.size());
    if (size > 0)
    {
        out.append((char*)buffer, size);
    }
}

void RtpMediaSource::onMediaFormat(const MediaFormat& fmt)
{

}

void RtpMediaSource::onMediaPacket(MediaPacketPtr& pkt)
{
    if (pkt->pts == AV_NOPTS_VALUE)
    {
        pkt->pts = pkt->dts;
    }

    m_pktQueue.push(pkt);
}

void RtpMediaSource::onMediaEvent(int event)
{

}


} /* namespace av */
